﻿using System;
using System.IO;
using System.Xml;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;

using SBPweb.PROvibe.Shared.Contracts.Content;

using SBPweb.PROvibe.Client.Framework.Contracts.Content;
using SBPweb.PROvibe.Client.Framework.Content.Connector;
using SBPweb.PROvibe.Client.Framework.Utilities.Collections;

namespace SBPweb.PROvibe.Client.Framework.Content
{

    public class SessionManager : OverridableDictionary<string, object>, ISessionManager
    {

        #region Private fields

        private ClientDescriptor clientDescriptor;
        private SessionPersistencyMode persistencyMode;
        private ConnectorClient connector;
        private int asyncTimeout = 5000;
        private object lockHelper = new object();

        #endregion

        #region Constructor logic

        public SessionManager()
            : this(ClientDescriptor.Unknown)
        {
        }

        public SessionManager(ClientDescriptor clientDescriptor)
            : this(clientDescriptor, new ConnectorClient(), SessionPersistencyMode.DirectSynchronous)
        {
        }

        public SessionManager(ClientDescriptor clientDescriptor, ConnectorClient connector)
            : this(clientDescriptor, connector, SessionPersistencyMode.DirectSynchronous)
        {
        }

        public SessionManager(ClientDescriptor clientDescriptor, ConnectorClient connector, SessionPersistencyMode persistencyMode)
        {
            this.clientDescriptor = clientDescriptor;
            this.ItemAdded += new EventHandler<DictionaryItemEventArgs<string, object>>(SessionManager_ItemAdded);
            this.ItemChanged += new EventHandler<DictionaryItemEventArgs<string, object>>(SessionManager_ItemChanged);
            this.ItemRemoved += new EventHandler<DictionaryItemEventArgs<string, object>>(SessionManager_ItemRemoved);
            this.connector = connector;
            this.connector.GetFullSessionBagCompleted += new EventHandler<GetFullSessionBagCompletedEventArgs>(connector_GetFullSessionBagCompleted);
            this.connector.AddSessionBagItemCompleted += new EventHandler<AsyncCompletedEventArgs>(connector_AddSessionBagItemCompleted);
            this.connector.ChangeSessionBagItemCompleted += new EventHandler<AsyncCompletedEventArgs>(connector_ChangeSessionBagItemCompleted);
            this.connector.RemoveSessionBagItemCompleted += new EventHandler<AsyncCompletedEventArgs>(connector_RemoveSessionBagItemCompleted);
            if ((this.persistencyMode = persistencyMode) != SessionPersistencyMode.OnDemand)
            {
                this.connector.GetFullSessionBagAsync(clientDescriptor);
            }
        }

        #endregion

        #region Property accessors

        public SessionPersistencyMode PersistencyMode
        {
            get
            {
                return persistencyMode;
            }
            set
            {
                persistencyMode = value;
            }
        }

        public ClientDescriptor Client
        {
            get
            {
                return clientDescriptor;
            }
        }

        #endregion

        #region Methods

        public TValue Get<TValue>(string key)
        {
            lock (lockHelper)
            {
                if (!base.ContainsKey(key))
                {
                    return default(TValue);
                }
                return (TValue)base[key];
            }
        }

        public void Put<TValue>(string key, TValue value)
        {
            lock (lockHelper)
            {
                if (base.ContainsKey(key))
                {
                    base[key] = value;
                }
                else
                {
                    base.Add(key, value);
                }
                if (typeof(TValue) == typeof(object))
                {
                    base.types[key] = value.GetType();
                }
                else
                {
                    base.types[key] = typeof(TValue);
                }
            }
        }

        public void RetrieveFromServer()
        {
            if (PersistencyMode == SessionPersistencyMode.DirectSynchronous)
            {
                throw new NotSupportedException("On-demand synchronization is not supported int Direct persistency mode!");
            }

            connector.GetFullSessionBagAsync(clientDescriptor);
        }

        public void StoreToServer()
        {
            if (PersistencyMode == SessionPersistencyMode.DirectSynchronous)
            {
                throw new NotSupportedException("On-demand synchronization is not supported int Direct persistency mode!");
            }

            connector.PutFullSessionBagAsync(clientDescriptor, base.dictionary);
        }

        public new IEnumerator GetEnumerator()
        {
            return base.GetEnumerator();
        }

        #region Event handlers

        private void SessionManager_ItemAdded(object sender, DictionaryItemEventArgs<string, object> e)
        {
            if (persistencyMode == SessionPersistencyMode.OnDemand)
            {
                return;
            }

            AutoResetEvent are = new AutoResetEvent(false);
            connector.AddSessionBagItemAsync(clientDescriptor, e.Key, e.Value, are);
            if (persistencyMode == SessionPersistencyMode.DirectSynchronous)
            {
                are.WaitOne(asyncTimeout);
            }
        }

        private void SessionManager_ItemChanged(object sender, DictionaryItemEventArgs<string, object> e)
        {
            if (persistencyMode == SessionPersistencyMode.OnDemand)
            {
                return;
            }
            AutoResetEvent are = new AutoResetEvent(false);
            connector.ChangeSessionBagItemAsync(clientDescriptor, e.Key, e.Value, are);
            if (persistencyMode == SessionPersistencyMode.DirectSynchronous)
            {
                are.WaitOne(asyncTimeout);
            }
        }

        private void SessionManager_ItemRemoved(object sender, DictionaryItemEventArgs<string, object> e)
        {
            if (persistencyMode == SessionPersistencyMode.OnDemand)
            {
                return;
            }
            AutoResetEvent are = new AutoResetEvent(false);
            connector.RemoveSessionBagItemAsync(clientDescriptor, e.Key, are);
            if (persistencyMode == SessionPersistencyMode.DirectSynchronous)
            {
                are.WaitOne(asyncTimeout);
            }
        }

        private void connector_GetFullSessionBagCompleted(object sender, GetFullSessionBagCompletedEventArgs e)
        {
            BeginInitialization();
            foreach (KeyValuePair<string, object> kvp in e.Result)
            {
                base.Add(kvp, true);
            }
            EndInitialization();
        }

        private void connector_AddSessionBagItemCompleted(object sender, AsyncCompletedEventArgs e)
        {
            AutoResetEvent are = e.UserState as AutoResetEvent;
            are.Set();
        }

        private void connector_ChangeSessionBagItemCompleted(object sender, AsyncCompletedEventArgs e)
        {
            AutoResetEvent are = e.UserState as AutoResetEvent;
            are.Set();
        }

        private void connector_RemoveSessionBagItemCompleted(object sender, AsyncCompletedEventArgs e)
        {
            AutoResetEvent are = e.UserState as AutoResetEvent;
            are.Set();
        }

        #endregion

        #endregion

    }

}